Skip to content

Introduction

Nano Kit is an ecosystem of lightweight, modular, and performant libraries for building modern web applications, built around a push-pull based reactivity system. Each library is designed to be small and tree-shakeable, while still offering rich DX and useful features.

import { signal, onMount, computed, effect } from '@nano_kit/store'
/* Create independent atomic stores */
const $count = signal(1)
/* Mountable: Run logic only when store has listeners */
onMount($count, () => {
console.log('Mounted: Store is active')
/* e.g., open websocket, start timer, etc. */
return () => {
console.log('Unmounted: Store is idle')
/* e.g., close websocket, stop timer */
}
})
/* Derive state (computed values are lazy & cached) */
const $double = computed(() => $count() * 2)
/* React to changes (triggers onMount) */
const unsub = effect(() => {
console.log(`Count: ${$count()}, Double: ${$double()}`)
})
/* Update triggers granular propagation */
$count(2)
/* Cleanup: removes listener and triggers onMount destructor */
unsub()
PackageDescription
@nano_kit/storeSignals-based state management library.
@nano_kit/queryData fetching and caching library, built on @nano_kit/store.
@nano_kit/routerRouting library, built on @nano_kit/store.
AdapterDescription
@nano_kit/reactReact bindings for @nano_kit/store.
@nano_kit/react-routerReact bindings for @nano_kit/router.

Nano Kit is built on the philosophy of Nano Stores and extends its core ideas:

  • Atomicity: Use independent, atomic stores for granular updates.
  • Mountable stores: Initialize resources only when the store is in use (on first listener) and clean up automatically.
  • Shift logic out of components: Move business logic to the store level.
  • Minimal footprint: Maintain a tiny core size that is fully tree-shakeable.

However, Nano Stores has some limitations:

  • Performance overhead
  • Suboptimal Developer Experience (DX)
  • Issues with SSR support

Nano Kit aims to fix these issues while keeping the same principles.

Adopting an ecosystem-first architecture provides a much smoother experience for developers than a stack of independent libraries. Because every part of the ecosystem is built on the same foundation, you get a consistent “feel” across the entire library. This shared core allows the tools to reuse internal logic, which drastically reduces your final bundle size.

Compare this to a standard setup like React Router + Redux + React Query. While these are great tools, they are effectively strangers to each other. They each have their own way of doing things and their own internal weight, often leading to duplicated code. Consequently, developers are forced to manually synchronize state and data between these independent tools inside the UI components, often resulting in bloated “glue code”.

Nano Kit takes a different approach. Because the router, data fetching, and state manager are engineered to work together from day one, they integrate deeply at the store level. This removes the need for messy “glue code” in your components and keeps your application logic clean, unified, and efficient. It makes the store layer portable and independent, allowing you to easily swap the UI layer.

import { browserNavigation, searchParams, searchParam } from '@nano_kit/router'
import { client, queryKey } from '@nano_kit/query'
import { effect } from '@nano_kit/store'
import { useSignal } from '@nano_kit/react'
/* Router */
const [$location, navigation] = browserNavigation({ /* routes */ })
const $searchParams = searchParams($location)
const $page = searchParam($searchParams, 'page', v => (v ? Number(v) : 1))
/* Query */
const { query } = client()
const [
$episodes,
$episodesError,
$episodesLoading
] = query(queryKey('episodes'), [$page], async (page) => {
/* Fetcher automatically receives the current page */
return fetch(`/api/episodes?page=${page}`).then(r => r.json())
})
/* Logic is fully decoupled from UI components */
effect(() => {
console.log('Current page:', $page())
console.log('Episodes:', $episodes())
})
/* UI components remain pure and logic-free. Example (React): */
function MyComponent() {
const page = useSignal($page)
const episodes = useSignal($episodes)
/* ... */
}
/* Changing the URL updates $page signal and automatically triggers re-fetch */
navigation.push('/episodes?page=2')

To achieve high performance, Nano Kit uses the push-pull algorithm from alien-signals as the core of its reactivity system. A dedicated fork named “Agera” was created to support additional required features.

The benchmark results below show how @nano_kit/store (Agera) compares to other popular state management libraries.

LibraryLatency avg (ns)Latency med (ns)Throughput avg (ops/s)Throughput med (ops/s)Samples
alien-signals318.57
± 0.34%
300.00
± 0.00
3262408
± 0.01%
3333333
± 0
3138998
@nano_kit/store338.98
± 0.26%
330.00
± 0.00
2986876
± 0.01%
3030303
± 0
2950008
svelte/store445.39
± 0.25%
410.00
± 0.00
2329147
± 0.02%
2439024
± 0
2245219
rxjs549.11
± 3.36%
470.00
± 20.00
2071142
± 0.02%
2127660
± 86843
1821114
nanostores1127.1
± 1.75%
1000.0
± 19.00
966136
± 0.02%
1000000
± 18646
887286
mobx4285.9
± 3.67%
3470.0
± 50.00
281427
± 0.04%
288184
± 4094
233326
valtio5401.6
± 8.52%
3910.0
± 160.00
240525
± 0.07%
255754
± 10912
185132
jotai10859
± 7.97%
7620.0
± 160.00
128036
± 0.06%
131234
± 2815
92089
effector31122
± 13.37%
18700
± 1110.0
53314
± 0.16%
53476
± 3123
32132

Benchmark was run on AMD Ryzen 5 PRO 3400G with Node.js v22.4.1

Comparison of bundle sizes for various implementations of the weather example bundled with Vite:

StackRaw SizeGzipped Size
React
+ nanostores + @nanostores/query + @nanostores/react
206.99 kB65.94 kB
React
+ @nano_kit/store + @nano_kit/query + @nano_kit/react
210.45 kB66.61 kB
React
+ @nano_kit/store + @nano_kit/query + @nano_kit/react + DI
211.60 kB66.95 kB
React
+ @tanstack/react-query
233.11 kB72.51 kB

Comparison of bundle sizes for various implementations of the rick-and-morty example bundled with Vite:

StackRaw SizeGzipped Size
React
+ @nano_kit/store + @nano_kit/query + @nano_kit/router
+ @nano_kit/react + @nano_kit/react-router
234.87 kB76.66 kB
React
+ @tanstack/react-query + @tanstack/react-router
333.78 kB107.28 kB